/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package oids.frontEnd.gameEngine;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import oids.frontEnd.components.Astroid;
import oids.frontEnd.components.BackGround;
import oids.frontEnd.components.Bullet;
import oids.frontEnd.components.Ship;
import oids.frontEnd.util.CollisionDetector;
import oids.frontEnd.util.ComponentStepCalculation;
import oids.frontEnd.util.GridWrapper;
import oids.frontEnd.util.Point2D;

/**
 *
 * @author CJ
 */
public class GameEngine extends JPanel implements ActionListener{
    
    protected final int width = 500;
    protected final int height = 500;

    Object obj = new Object();

    protected final int astroidStartSize = 40;

    protected final int startSpaceAroundSpace = 100;

    protected final int firingCoolDown = 10;
    protected long lastFireTime;

    protected Ship ship;
    protected BackGround backGround;

    protected double score;
    protected long startTime;
    protected long timeAlive;

    protected long gameOverStateTime;
    protected final long gameOverShowTime = 5000;
    protected boolean firstGameOverShow;

    protected boolean gameOver;

    protected ArrayList<Astroid> asteroids;
    protected ArrayList<Bullet> bullets;

    protected GridWrapper gridWrapper= new GridWrapper(this.width, this.height);

    protected int level;

    public GameEngine(){
        initComponents();
        initGraphics();
    }

    public boolean getGameOver(){
        return this.gameOver;
    }

    protected void initGraphics(){
        setPreferredSize(new Dimension(width, height));
        setFocusable(true);
    }

    protected void initComponents(){
        this.ship = new Ship(width/2, height/2);
        addKeyListener(this.ship);

        this.backGround = new BackGround(width, height);

        this.gameOver = false;
        
        this.asteroids = new ArrayList<Astroid>();
        this.bullets = new ArrayList<Bullet>();
        this.level = 1;
        initAstroids();
        initBullets();

        this.startTime = System.currentTimeMillis();
        this.score = 0;
        this.lastFireTime = System.currentTimeMillis();
        this.firstGameOverShow = true;
    }

    protected void initBullets(){
        this.bullets.clear();
    }

    protected void initAstroids(){
        for(int i = this.level + 1; i > 0; --i){
//            int xPlacement = (int)(Math.random() * this.width);
//            int yPlacement = (int)(Math.random() * this.height);
//            if(Math.abs(this.width - xPlacement) < 50)
//                xPlacement += (Math.random() > 0.5) ? this.startSpaceAroundSpace : -this.startSpaceAroundSpace;
//            if(Math.abs(this.height - yPlacement) < 50)
//                yPlacement += (Math.random() > 0.5) ? this.startSpaceAroundSpace : -this.startSpaceAroundSpace;
            Point2D temp = getRandomCoords();
            int xPlacement = temp.getX();
            int yPlacement = temp.getY();
            this.asteroids.add(new Astroid(xPlacement, yPlacement, this.astroidStartSize));
        }
    }

    public void actionPerformed(ActionEvent e){
        if(this.asteroids.size() < 1)
            advanceLevel();
        if(gameOver){
            repaint();
            return;
        }
        addBullets();
        stepAll();
        checkCollisions();
        outOfBoundsFix();
        if(!gameOver){
            this.timeAlive = System.currentTimeMillis() - this.startTime;
        }
        repaint();
    }

    @Override
    public void paintComponent(Graphics g){
        super.paintComponent(g);

//        if(gameOver && System.currentTimeMillis() - this.gameOverStateTime > this.gameOverShowTime){
//            if(this.firstGameOverShow){
//                this.firstGameOverShow = false;
//                initHighScore();
//            }
//            super.paintComponents(g);
//            return;
//        }
        paintBackGround(g);
        paintScore(g);
        paintShip(g);
        paintBullets(g);
        paintAstroids(g);
        if(gameOver){
            paintGameOver(g);
        }
    }

    protected void advanceLevel(){
        this.level++;
        this.initAstroids();
        this.bullets.clear();
    }

    protected void paintGameOver(Graphics g){
    }

    protected Point2D getRandomCoords(){
        int xPlacement = (int)(Math.random() * this.width);
        int yPlacement = (int)(Math.random() * this.height);
        if(Math.abs(this.ship.getX() - xPlacement) < 50)
            xPlacement += (Math.random() > 0.5) ? this.startSpaceAroundSpace : -this.startSpaceAroundSpace;
        if(Math.abs(this.ship.getY() - yPlacement) < 50)
            yPlacement += (Math.random() > 0.5) ? this.startSpaceAroundSpace : -this.startSpaceAroundSpace;
        return new Point2D(xPlacement, yPlacement);
    }
    
    protected void outOfBoundsFix(){
        fixBullets();
        fixAsteroids();
        fixShip();
    }

    protected void fixShip(){
        if(this.gridWrapper.outOfBounds(this.ship))
            this.gridWrapper.replacePosition(this.ship);
    }

    protected void fixAsteroids(){
        synchronized(obj){
            for(int i = 0; i < this.asteroids.size(); ++i)
                if(this.gridWrapper.outOfBounds(this.asteroids.get(i)))
                    this.gridWrapper.replacePosition(this.asteroids.get(i));
        }
    }

    protected void fixBullets(){
        synchronized(obj){
            for(int i = 0; i < this.bullets.size(); ++i)
                if(this.gridWrapper.outOfBounds(this.bullets.get(i)))
                    this.bullets.remove(this.bullets.get(i));
        }
    }

    protected void checkCollisions(){
        checkBulletCollisions();
        checkShipCollision();
    }

    protected void checkBulletCollisions(){
        synchronized(obj){
            for(int i = 0; i < this.bullets.size(); ++i)
                for(int j = 0; j < this.asteroids.size(); ++j)
                        if(CollisionDetector.detectCollisions(this.bullets.get(i), this.asteroids.get(j))){
                            this.bullets.remove(this.bullets.get(i));
                            removeAndBreak(this.asteroids.get(j));
                            break;
                        }
        }
    }

    protected void removeAndBreak(Astroid ast){
        this.asteroids.remove(ast);
        scoreAstroid(ast);
        if(ast.getSize()/2 < 10){
            return;
        }
        this.asteroids.add(new Astroid(ast.getX(), ast.getY(), ast.getSize()/2));
        this.asteroids.add(new Astroid(ast.getX(), ast.getY(), ast.getSize()/2));
    }

    protected void scoreAstroid(Astroid ast){
        this.score += ast.getSize();
    }

    protected void checkShipCollision(){
        for(int i = 0; i < this.asteroids.size(); ++i)
            if(CollisionDetector.detectCollisions(ship, this.asteroids.get(i))){
                this.gameOver = true;
                this.gameOverStateTime = System.currentTimeMillis();
                Astroid ast = this.asteroids.get(i);
                System.out.println("Collision Detected");
                System.out.println("Ship X, Y: " + ship.getX() + " | " + ship.getY());
                System.out.println("Astroid X, Y: " + ast.getX() + " | " + ast.getY());
                System.out.println("Size ship, astroid: " + ship.getSize() + " | " + ast.getSize());
            }
    }

    protected void addBullets(){
        if(this.ship.getFiring() && System.currentTimeMillis() - this.lastFireTime > this.firingCoolDown){
            this.lastFireTime = System.currentTimeMillis();
            this.bullets.add(new Bullet(
                    ComponentStepCalculation.getStartingPoint(this.ship.getX(),
                    this.ship.getY(),
                    this.ship.getRad()), this.ship.getRad()));
        }
    }

    protected void stepAll(){
        stepBullets();
        stepShip();
        stepAstroids();
    }

    protected void stepAstroids(){
        for(int i = 0; i < this.asteroids.size(); ++i)
            this.asteroids.get(i).step();
    }

    protected void stepShip(){
        this.ship.step();
    }

    protected void stepBullets(){
        for(int i = 0;i < bullets.size();++i){
            bullets.get(i).step();
        }
    }

    protected void paintBullets(Graphics g){
        for(int i = 0; i < this.bullets.size(); ++i){
            this.bullets.get(i).draw(g);
        }
    }

    protected void paintAstroids(Graphics g){
        for(int i = 0;i < this.asteroids.size(); ++i){
            this.asteroids.get(i).draw(g);
        }
    }

    protected void paintShip(Graphics g){
        this.ship.draw(g);
    }

    protected void paintBackGround(Graphics g){
        this.backGround.draw(g);
    }

    protected void paintScore(Graphics g){
        g.setColor(Color.white);
        g.drawString(Integer.toString(this.level) + " :Level", 50, 30);
        g.drawString(this.timeAlive + " :ms Alive", 50, 40);
        g.drawString(Double.toString(this.score) + " :Score", 50, 50);
    }

}
